package poi;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Logger;
import exceptions.WrongIDException;
import javax.faces.context.FacesContext;


public class POISearcher {

	private static Logger log = Logger.getLogger("POISystem.POISearcher");
	
	private static final long serialVersionUID = -3351411561062971138L;
	private LinkedList<GeoInfo> data = null;
	private POIDatabase db = null;
	private  String searchName;
	private String searchType;
	private int searchDistance;
	private int homePointID;
	private LinkedList<GeoInfo> myList;
	private LinkedList<GeoInfo> searchResult;
	private boolean resultPresented;
	private boolean smthFound;
	private GlobalDataKeeper globalDK = null;
	
	/**
	 * @return the searchResult
	 */
	public LinkedList<GeoInfo> getSearchResult() {
		return searchResult;
	}

	/**
	 * @return the resultPresented
	 */
	public boolean getResultPresented() {
		return resultPresented;
	}

	/**
	 * @return the homePointID
	 */
	public int getHomePointID() {
		return homePointID;
	}

	/**
	 * @param homePointID the homePointID to set
	 */
	public void setHomePointID(int homePointID) {
		this.homePointID = homePointID;
	}

	/**
	 * @return the searchName
	 */
	public String getSearchName() {
		return searchName;
	}

	/**
	 * @param searchName the searchName to set
	 */
	public void setSearchName(String searchName) {
		this.searchName = searchName;
	}

	/**
	 * @return the searchDistance
	 */
	public int getSearchDistance() {
		return searchDistance;
	}

	/**
	 * @param searchDistance the searchDistance to set
	 */
	public void setSearchDistance(int searchDistance) {
		this.searchDistance = searchDistance;
	}


	/**
	 * @return the searchType
	 */
	public String getSearchType() {
		return searchType;
	}

	/**
	 * @param searchType the searchType to set
	 */
	public void setSearchType(String searchType) {
		this.searchType = searchType;
	}

	/**
	 * @return the myList
	 */
	public LinkedList<GeoInfo> getMyList() {
		return myList;
	}

	/**
	 * @return the smthFound
	 */
	public boolean isSmthFound() {
		return smthFound;
	}

	/**
	 * @return the data
	 */
	public LinkedList<GeoInfo> getData() {
		return data;
	}

	@SuppressWarnings("deprecation")
	private void initObject(String csvFileName)
	{
		LogFormatter.setLogFormat();
		FacesContext facesContext = FacesContext.getCurrentInstance();
		globalDK = (GlobalDataKeeper) facesContext.getApplication().getVariableResolver().resolveVariable(facesContext, "globalDataKeeper");
		db = globalDK.getData();
		data = db.buildList(null);
		searchName = "";
		searchType = "My";
		searchDistance = 100;
		smthFound = false;
		homePointID = 0;
		resultPresented = false;
		Iterator<GeoInfo> it = data.iterator();
		myList = new LinkedList<GeoInfo>();
		while (it.hasNext()) {
			GeoInfo val = it.next();
			if (val.getLocationType() == LocationType.MY) {
				myList.add(val);
			}
		}
		homePointID = ((GeoInfo) myList.iterator().next()).getPointID();
		searchResult = new GeoInfoList();
		log.info("POISearch object created.");
	}
	
	public POISearcher(String csvFileName) {
		LogFormatter.setLogFormat();
		searchName = "";
		searchType = "My";
		searchDistance = 100;
		smthFound = false;
		db = new POIDatabase();
		db.connectSource(null);
		data = db.buildList(null);
		homePointID = 0;
		resultPresented = false;
		Iterator<GeoInfo> it = data.iterator();
		myList = new LinkedList<GeoInfo>();
		while (it.hasNext()) {
			GeoInfo val = it.next();
			if (val.getLocationType() == LocationType.MY) {
				myList.add(val);
			}
		}
		homePointID = ((GeoInfo) myList.iterator().next()).getPointID();
		searchResult = new GeoInfoList();
		log.info("POISearch object created.");
	}

	public POISearcher() {
		initObject("POISystem\\poidata.csv");
	}
	
	
	private double deg2rad(double deg) {
		  return (deg * Math.PI / 180.0);
		}

	private double rad2deg(double rad) {
		  return (rad * 180.0 / Math.PI);
		}

	
	private double calculateDistance(GeoInfo point1, GeoInfo point2) {
		  double theta = point1.getLongtitude() - point2.getLongtitude();
		  double dist = Math.sin(deg2rad(point1.getLatitude())) * Math.sin(deg2rad(point2.getLatitude())) + Math.cos(deg2rad(point1.getLatitude())) * Math.cos(deg2rad(point2.getLatitude())) * Math.cos(deg2rad(theta));
		  dist = Math.acos(dist);
		  dist = rad2deg(dist);
		  dist = dist * 60 * 1.853159616;
		  return dist;
		}
	
	public String search() throws WrongIDException
	{
		log.info("Searching started.");
		log.info("Search parameters:");
		log.info("Name: " + searchName);
		log.info("Distance :" + searchDistance);
		log.info("Type:" + searchType);
		log.info("Home ID:" + homePointID);
		log.info("--------------------------");
		
		searchResult = new LinkedList<GeoInfo>();
		
		GeoInfo homeItem = null;
		homeItem = db.getItemByID(homePointID); 
		if (homeItem == null) {
			log.severe("Home POI not found.");
			throw new exceptions.WrongIDException();			
		}
		if (searchDistance <= 0) {
			searchDistance = 100;
		}
		Iterator<GeoInfo> it = data.iterator();
		while (it.hasNext()) {
			boolean fnd = true;
			GeoInfo val = it.next();
			fnd = (homeItem.getPointID() != val.getPointID())
				  && ((searchName == "") || (val.getLocationName().indexOf(searchName) != -1))
				  && (calculateDistance(homeItem, val) <= searchDistance)
				  && (searchType.equals(GeoInfo.locationTypeNameToString(val.getLocationType())));
			
			if (fnd) {
				log.info("Found \"" + val.getLocationName() + "\" object");
				searchResult.add(val);
			}
		}		
		log.info("Search finished.");
		resultPresented = true;
		smthFound = searchResult.size() > 0;
		return "Done!";
	}

	public String clearResults() {
		resultPresented = false;
		return "reset";
	}
	
	protected void finalize() {
		log.info("POISearch object deleted.");
	}
	
}